import { DataTypes, Op } from 'sequelize';
import { sequelize } from '../config/database.js';

const RedeemCode = sequelize.define('RedeemCode', {
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  code: {
    type: DataTypes.STRING(7),
    allowNull: false,
    comment: '兑换码'
  },
  quota_type: {
    type: DataTypes.ENUM('general', 'domain', 'expert'),
    allowNull: false,
    comment: '额度类型'
  },
  target_id: {
    type: DataTypes.INTEGER,
    allowNull: true,
    comment: '目标ID（领域ID或专家ID）'
  },
  redeem_count: {
    type: DataTypes.INTEGER,
    allowNull: false,
    comment: '可兑换次数'
  },
  expires_at: {
    type: DataTypes.DATE,
    allowNull: true,
    comment: '过期时间'
  },
  is_used: {
    type: DataTypes.BOOLEAN,
    defaultValue: false,
    comment: '是否已使用'
  },
  used_by: {
    type: DataTypes.INTEGER,
    allowNull: true,
    comment: '使用者用户ID'
  },
  used_at: {
    type: DataTypes.DATE,
    allowNull: true,
    comment: '使用时间'
  },
  created_by: {
    type: DataTypes.INTEGER,
    allowNull: false,
    comment: '创建人（管理员ID）'
  },
  batch: {
    type: DataTypes.STRING(50),
    allowNull: true,
    comment: '批次标识'
  },
  created_at: {
    type: DataTypes.DATE,
    defaultValue: DataTypes.NOW
  },
  updated_at: {
    type: DataTypes.DATE,
    defaultValue: DataTypes.NOW
  }
}, {
  tableName: 'redeem_codes',
  timestamps: true,
  createdAt: 'created_at',
  updatedAt: 'updated_at',
  indexes: [
    {
      name: 'idx_redeem_codes_code',//
      unique: true,
      fields: ['code'],
    },
    {
      name: 'idx_redeem_codes_quota_type',
      fields: ['quota_type'],
    },
    {
      name: 'idx_redeem_codes_target_id',
      fields: ['target_id'],
    },
    {
      name: 'idx_redeem_codes_is_used',
      fields: ['is_used'],
    },
    {
      name: 'idx_redeem_codes_expires_at',
      fields: ['expires_at'],
    },
    {
      name: 'idx_redeem_codes_created_by',
      fields: ['created_by'],
    },
    {
      name: 'idx_redeem_codes_batch',
      fields: ['batch'],
    },
    {
      name: 'idx_redeem_codes_user_batch',
      fields: ['used_by', 'batch'],
    },
  ],
});

// 最大重试次数，防止无限循环
const MAX_RETRIES = 5; 

// 实例方法
RedeemCode.prototype.toJSON = function() {
  const values = { ...this.get() };
  return values;
};

RedeemCode.prototype.canRedeem = function() {
  if (this.is_used) return false;
  if (this.expires_at && new Date() > this.expires_at) return false;
  return true;
};

RedeemCode.prototype.redeem = async function(userId) {
  if (!this.canRedeem()) {
    throw new Error('兑换码已使用或已过期');
  }
  
  this.is_used = true;
  this.used_by = userId;
  this.used_at = new Date();
  await this.save();
  return this;
};

// 类方法
RedeemCode.findByCode = function(code) {
  return this.findOne({ 
    where: { code },
    include: [
      { association: 'domain', attributes: ['id', 'name'] },
      { association: 'expert', attributes: ['id', 'nickname'] },
      { association: 'creator', attributes: ['id', 'username'] }
    ]
  });
};

RedeemCode.generateCode = function() {
  const now = new Date();
  const year = now.getFullYear();
  const month = now.getMonth() + 1; // getMonth() returns 0-11

  // 年份编码：2025=A, 2026=B, 2027=C...
  const yearCode = String.fromCharCode(65 + (year - 2025)); // A=65

  // 月份编码：1月=J, 2月=K, 3月=L, 4月=M, 5月=N, 6月=P, 7月=Q, 8月=R, 9月=S, 10月=T, 11月=U, 12月=V
  // 跳过I和O，所以使用J开始
  const monthCodes = ['J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'];
  const monthCode = monthCodes[month - 1];

  // 随机字符集：排除I、O、1、0
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';

  // 生成5位随机字符
  let randomPart = '';
  for (let i = 0; i < 5; i++) {
    randomPart += chars.charAt(Math.floor(Math.random() * chars.length));
  }

  return yearCode + monthCode + randomPart;
};

// 生成批次标识
RedeemCode.generateBatch = function() {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, '0');
  const day = String(now.getDate()).padStart(2, '0');
  const hour = String(now.getHours()).padStart(2, '0');
  const minute = String(now.getMinutes()).padStart(2, '0');

  // 格式：BATCH_YYYYMMDD_HHMM
  return `BATCH_${year}${month}${day}_${hour}${minute}`;
};

RedeemCode.createCode = async function(codeData) {
  if (!codeData.code) {
    let retries = 0;
    while (retries < MAX_RETRIES) {
      try {
        codeData.code = this.generateCode();
        return await this.create(codeData);
      } catch (error) {
        // 检查是否是唯一约束错误（例如，PostgreSQL的错误码23505）
        // 不同的数据库可能有不同的错误码，这里以Sequelize的错误类型为例
        if (error.name === 'SequelizeUniqueConstraintError') {
          console.warn(`Generated duplicate redeem code '${codeData.code}'. Retrying... (${retries + 1}/${MAX_RETRIES})`);
          retries++;
        } else {
          throw error; // 非唯一约束错误，直接抛出
        }
      }
    }
    throw new Error('Failed to create unique redeem code after multiple retries.');
  }
  return await this.create(codeData);
};

RedeemCode.createBatch = async function(batchData) {
  const { count, ...baseData } = batchData;
  const codesToCreate = [];
  
  for (let i = 0; i < count; i++) {
    let retries = 0;
    let uniqueCodeFound = false;
    while (retries < MAX_RETRIES && !uniqueCodeFound) {
      const code = {
        ...baseData,
        code: this.generateCode()
      };
      // 在这里不直接创建，而是先检查是否已存在，尽量避免批量插入时才发现重复
      // 更严谨的做法是在批量插入前进行预检查，或者依赖数据库的唯一约束和事务回滚
      // 为了简化，这里依赖 generateCode 的随机性和数据库的唯一约束
      codesToCreate.push(code);
      uniqueCodeFound = true; // 假设生成的是唯一的，如果bulkCreate失败会回滚
      retries++;
    }
    if (!uniqueCodeFound) {
      throw new Error('Failed to generate a unique code for batch creation after multiple retries.');
    }
  }
  
  // 使用事务确保批量创建的原子性
  const t = await sequelize.transaction();
  try {
    const createdCodes = await this.bulkCreate(codesToCreate, { transaction: t });
    await t.commit();
    return createdCodes;
  } catch (error) {
    await t.rollback();
    // 对于批量插入的唯一约束错误，需要更细致的处理，这里简化为抛出
    // 实际应用中可能需要识别是哪个兑换码重复，并仅重试该兑换码的生成和插入
    if (error.name === 'SequelizeUniqueConstraintError') {
      console.error('Batch creation failed due to duplicate redeem code. Consider regenerating and retrying the failed ones.');
      throw new Error('One or more generated redeem codes were duplicates in batch creation. Please retry.');
    }
    throw error;
  }
};

RedeemCode.getList = function(options = {}) {
  const { page = 1, limit = 20, quotaType, isUsed, createdBy, batch } = options;
  const where = {};

  if (quotaType) {
    where.quota_type = quotaType;
  }

  if (typeof isUsed === 'boolean') {
    where.is_used = isUsed;
  }

  if (createdBy) {
    where.created_by = createdBy;
  }

  if (batch) {
    where.batch = batch;
  }
  
  return this.findAndCountAll({
    where,
    include: [
      { association: 'domain', attributes: ['id', 'name'] },
      { association: 'expert', attributes: ['id', 'nickname'] },
      { association: 'creator', attributes: ['id', 'username'] }
    ],
    order: [['created_at', 'DESC']],
    limit,
    offset: (page - 1) * limit
  });
};

// 检查用户是否已在指定批次中兑换过兑换码
RedeemCode.hasUserRedeemedInBatch = async function(userId, batch) {
  if (!batch) return false; // 如果没有批次，则不限制

  const existingRedemption = await this.findOne({
    where: {
      used_by: userId,
      batch: batch,
      is_used: true
    }
  });

  return !!existingRedemption;
};

// 获取批次列表（用于前端筛选）
RedeemCode.getBatchList = function() {
  return this.findAll({
    attributes: ['batch'],
    where: {
      batch: {
        [Op.ne]: null
      }
    },
    group: ['batch'],
    order: [['batch', 'DESC']]
  });
};

export default RedeemCode;
